/**
 * 
 */

var thumbnailPlots = {};
var currentPlot;
var currentCycle;
var currentDay = null;

var GAPI_KEY = 'AIzaSyBKJoNs0TyF-2jukeg2gpTpNQP_RLleQtE';
var GAPI_CLIENT_ID = '488141249597.apps.googleusercontent.com';
var GAPI_SCOPES = [ 'https://www.googleapis.com/auth/drive.appdata', 'https://www.googleapis.com/auth/userinfo#email' ];

$(function() {
  // also tricks cookie blocking policy to enable local storage
  document.cookie = "ovu=true";
  $('.input-group.date').datepicker({
    autoclose : true,
    todayHighlight : true,
    format : "dd-mm-yyyy"
  });
  $('#ovu-cycle-details-change-start-date, #ovu-cycle-details-change-end-date').datepicker({
    autoclose : true,
    todayHighlight : true,
    format : "dd-mm-yyyy"
  }).on("changeDate", ovuCycleDetailsDatepickerChangeDate);
  $(".tooltip-holder").tooltip({});
  $("#ovu-cycle-chart").bind("jqplotDataClick", ovuCycleChartPointSelected);
  $("#ovu-day-add").on("show.bs.modal", ovuDayAddShow);
  $("#ovu-day-details").on("show.bs.modal", ovuDayDetailsShow);
  $('#ovu-day-add-temperature, #ovu-day-mod-temperature').keypress(function(event) {
    var k = event.which;
    if (!(k < 10 || k == 44 || k == 46 || (k >= 48 && k < 58))) {
      event.preventDefault();
    }
  });
});

function ovuShowLocalStorageNotice() {
  $("#ovu-jumbo-login-buttons").css("display", "none");
  $("#ovu-local-storage-notice").css("display", "");
}

function createUUID() {
  // http://www.ietf.org/rfc/rfc4122.txt
  var s = [];
  var hexDigits = "0123456789abcdef";
  for (var i = 0; i < 36; i++) {
    s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
  }
  s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
  s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the
  // clock_seq_hi_and_reserved
  // to 01
  s[8] = s[13] = s[18] = s[23] = "-";
  var uuid = s.join("");
  return uuid;
}

function ovuInitTestMode() {
  $("#ovu-jumbotron").css("display", "none");
  $("#ovu-local-storage-warning").css("display", "");
  ovuPruneNonLocalCycles();
  ovuShowCycleList();
}

function ovuPruneNonLocalCycles() {
  var keysToDelete = [];
  $.each(amplify.store(), function(key, value) {
    if (key.match("^cycle\.")) {
      var cycle = $.evalJSON(value);
      if (cycle.googId) {
        keysToDelete.push(key);
      }
    }
  });
  $.each(keysToDelete, function(index, key) {
    amplify.store(key, null);
  });
}

// loads cycles from local storage
function ovuLoadCycles(googOnly) {
  var cycles = [];
  $.each(amplify.store(), function(key, value) {
    if (key.match("^cycle\.")) {
      var cycle = $.evalJSON(value);
      if (!googOnly || cycle.googId) {
        cycles.push(cycle);
      }
    }
  });
  return cycles;
}

function ovuRefreshCycleList() {
  $.each(thumbnailPlots, function(key, plot) {
    plot.destroy();
  });
  $('#ovu-cycle-list').empty();
  var cycles = ovuLoadCycles(gapi.auth.getToken() != null);
  cycles.sort(function(a, b) {
    return a.startDate ? (b.startDate ? b.startDate - a.startDate : -1) : 1;
  });
  $.each(cycles, function(index, cycle) {
    ovuCreateCycleThumbnail(cycle);
    return true;
  });
}

function ovuCreateCycleThumbnail(cycle) {
  var thumbnail = $('<a class="thumbnail">');
  thumbnail.click(function() {
    ovuCycleSelected(cycle.id);
  });
  var chart = $('<div id="ovu-cycle-thumbnail-' + cycle.id + '" class="chart">');
  thumbnail.append(chart);
  var caption = $('<div class="caption">');
  caption.append($('<p>', {
    text : ovuGetCycleLabel(cycle)
  }));
  thumbnail.append(caption);
  var column = $('<div class="col-sm-6 col-md-4">');
  column.append(thumbnail);
  $('#ovu-cycle-list').append(column);
  if (cycle.days !== undefined && cycle.days.length > 0) {
    ovuDrawCycleThumbnail(cycle);
  } else {
    $("#ovu-cycle-thumbnail-" + cycle.id).html("<p>Pusty</p>");
  }
}

function ovuDrawCycleThumbnail(cycle) {
  var plot = thumbnailPlots["" + cycle.id];
  if (plot !== undefined) {
    plot.destroy();
  }
  var data = [ [] ];
  var minTemperature = 100;
  var maxTemperature = 0;
  if (cycle.days !== undefined) {
    $.each(cycle.days, function(index, day) {
      data[0].push([ day.date, day.temperature ]);
      minTemperature = Math.min(minTemperature, day.temperature);
      maxTemperature = Math.max(maxTemperature, day.temperature);
    });
  }
  var yscalemin = Math.floor(minTemperature * 10 - 3) / 10;
  var yscalemax = Math.max(Math.ceil(maxTemperature * 10 + 3) / 10, yscalemin + 2);
  var options = {
    axes : {
      xaxis : {
        renderer : $.jqplot.DateAxisRenderer,
        tickInterval : '1 day',
        tickOptions : {
          showLabel : false
        }
      },
      yaxis : {
        min : yscalemin,
        max : yscalemax,
        tickOptions : {
          showLabel : false
        }
      }
    }
  /*
   * , series : [ { lineWidth : 4, markerOptions : { style : 'square' } } ]
   */
  };
  thumbnailPlots["" + cycle.id] = $.jqplot("ovu-cycle-thumbnail-" + cycle.id, data, options);
}

function showErrorAlert(text) {
  $("#ovu-breadcrumb").after(
      '<div class="alert alert-danger alert-dismissable">' + text + ' Sprawdź połączenie z internetem i spróbuj ponownie.'
          + '<a class="close" data-dismiss="alert" href="#" aria-hidden="true">&times;</a></div>');
  $(".alert-danger").alert();
}

function ovuAddCycle() {
  var cycle = {};
  cycle.id = createUUID();
  cycle.startDate = ovuParseUTCDate($("#ovu-cycle-add-start-date").val());
  ovuGetSaveFn()(cycle, function(resp) {
    $("#ovu-cycle-add").modal("hide");
    if (!resp.error) {
      ovuRefreshCycleList();
    } else {
      showErrorAlert('Wystąpił błąd podczas dodawania cyklu.');
    }
  });
}

function ovuGetCycleLabel(cycle) {
  var label = ovuUTCDateToStr(cycle.startDate);
  label += " - ";
  label += cycle.endDate !== undefined ? ovuUTCDateToStr(cycle.endDate) : "?";
  return label;
}

function ovuCycleSelected(id) {
  currentCycle = $.evalJSON(amplify.store("cycle." + id));
  if (currentCycle.googId) {
    gapi.client.drive.files.get({
      'fileId' : currentCycle.googId
    }).execute(function(file) {
      if (!file.error) {
        if (!currentCycle.modified || new Date(currentCycle.modified) < new Date(file.modifiedDate)) {
          $.ajax({
            url : file.downloadUrl,
            beforeSend : function(xhr) {
              var accessToken = gapi.auth.getToken().access_token;
              xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
            },
            success : function(cycle) {
              cycle.googId = file.id;
              cycle.modified = file.modifiedDate;
              currentCycle = cycle;
              ovuSaveCycle(currentCycle);
              ovuDisplayCurrentCycle();
            }
          });
        } else {
          ovuDisplayCurrentCycle();
        }
      } else {
        showErrorAlert('Wystąpił błąd podczas pobierania danych o cyklu.');
        delete currentCycle.googId;
        ovuDisplayCurrentCycle();
      }
    });
  } else {
    ovuDisplayCurrentCycle();
  }
}

function ovuDisplayCurrentCycle() {
  $("#ovu-cycle-list-row").css("display", "none");
  $("#ovu-cycle-details-row").css("display", "");

  $("#ovu-breadcrumb").html('<li><a href="javascript:void(0)" onclick="ovuShowCycleList()">Lista cykli</a></li>').append(
      '<li class="active">' + ovuGetCycleLabel(currentCycle) + '</li>');

  var startDateStr = ovuUTCDateToStr(currentCycle.startDate);
  $("#ovu-cycle-details-start-date").html(startDateStr);
  $("#ovu-cycle-details-change-start-date").datepicker("setDate", ovuParseDate(startDateStr));
  $("#ovu-cycle-details-change-start-date").datepicker("update", startDateStr);

  if (currentCycle.endDate !== undefined) {
    $("#ovu-cycle-details-end-date").html(ovuUTCDateToStr(currentCycle.endDate));
  } else {
    $("#ovu-cycle-details-end-date").html('<span class="missing">Brak</span>');
  }

  ovuUpdateDetailsNumDays();
  ovuChartUpdate();
}

function ovuUpdateDetailsNumDays() {
  if (currentCycle.endDate !== undefined) {
    var diff = currentCycle.endDate - currentCycle.startDate;
    $("#ovu-cycle-details-num-days").html(diff / (1000 * 60 * 60 * 24) + 1);
  } else {
    $("#ovu-cycle-details-num-days").html('<span class="missing">Data ostatniego dnia cyklu nie została ustawiona</span>');
  }
}

function ovuCycleDetailsDatepickerChangeDate(event) {
  var dateStr = ovuDateToStr(event.date);
  var utcDate = ovuParseUTCDate(dateStr);
  var suffix = event.currentTarget.id.substr("ovu-cycle-details-change-".length);
  var changed = false;
  if (suffix == "start-date") {
    if (utcDate != currentCycle.startDate) {
      currentCycle.startDate = utcDate;
      changed = true;
    }
  } else if (suffix == "end-date") {
    if (utcDate != currentCycle.endDate) {
      currentCycle.endDate = utcDate;
      changed = true;
    }
  }
  if (changed) {
    ovuGetSaveFn()(currentCycle, function(resp) {
      if (!resp.error) {
        $("#ovu-cycle-details-" + suffix).html(dateStr);
        ovuUpdateDetailsNumDays();
      } else {
        showErrorAlert('Wystąpił błąd podczas zapisywania szczegółów cyklu.');
      }
    });
  }
}

function ovuShowCycleList() {
  $("#ovu-breadcrumb").html('<li class="active">Lista cykli</li>').css("display", "");
  $("#ovu-cycle-list-row").css("display", "");
  $("#ovu-cycle-details-row").css("display", "none");
  ovuRefreshCycleList();
}

function ovuDeleteCycle() {
  if (currentCycle.googId) {
    var request = gapi.client.request({
      "path" : "/drive/v2/files/" + currentCycle.googId,
      "method" : "DELETE"
    }).execute(function(resp) {
      if (!resp || !resp.error) {
        amplify.store("cycle." + currentCycle.id, null);
        $("#ovu-cycle-delete").modal("hide");
        ovuShowCycleList();
      } else {
        showErrorAlert('Wystąpił błąd podczas usuwania cyklu.');
      }
    });
  } else {
    $("#ovu-cycle-delete").modal("hide");
    ovuShowCycleList();
  }
}

function ovuSaveCycle(cycle) {
  var cycleStr = $.toJSON(cycle);
  amplify.store("cycle." + cycle.id, cycleStr);
}

function ovuDayAdd() {
  var day = {};
  day.date = ovuParseUTCDate($("#ovu-day-add-date").val());
  day.temperature = parseFloat($("#ovu-day-add-temperature").val());
  if (currentCycle.days === undefined) {
    currentCycle.days = [];
  }
  currentCycle.days.push(day);
  ovuGetSaveFn()(currentCycle, function(resp) {
    $("#ovu-day-add").modal("hide");
    if (!resp.error) {
      ovuChartUpdate();
    } else {
      showErrorAlert('Wystąpił błąd podczas dodawania dnia cyklu.');
    }
  });
}

function ovuDayAddShow(event) {
  var t = null;
  var days = currentCycle.days;
  if (days && days.length > 0) {
    t = days[days.length - 1].temperature;
  }
  if (!t) {
    t = 36.6;
  }
  $("#ovu-day-add-date").val(ovuDateToStr(new Date()));
  $("#ovu-day-add-temperature").val(t);
}

function ovuDayDetailsShow(event) {
  var day = $.grep(currentCycle.days, function(day) {
    return day.date == currentDay;
  })[0];
  $("#ovu-day-mod-date").val(ovuUTCDateToStr(day.date));
  $("#ovu-day-mod-temperature").val(day.temperature);
}

function ovuDayModify() {
  var day = $.grep(currentCycle.days, function(day) {
    return day.date == currentDay;
  })[0];
  var newTemperature = $("#ovu-day-mod-temperature").val();
  if (day.temperature != newTemperature) {
    day.temperature = newTemperature;
  }
  ovuGetSaveFn()(currentCycle, function(resp) {
    currentDay = null;
    $("#ovu-day-details").modal("hide");
    if (!resp.error) {
      ovuChartUpdate();
    } else {
      showErrorAlert('Wystąpił błąd podczas zapisywania szczegółów dnia cyklu.');
    }
  });
}

function ovuDayDelete() {
  var newDays = [];
  $.each(currentCycle.days, function(index, day) {
    if (day.date != currentDay) {
      newDays.push(day);
    }
  });
  currentCycle.days = newDays;
  ovuGetSaveFn()(currentCycle, function(resp) {
    currentDay = null;
    $("#ovu-day-details").modal("hide");
    if (!resp.error) {
      ovuChartUpdate();
    } else {
      showErrorAlert('Wystąpił błąd podczas usuwania dnia cyklu.');
    }
  });
}

function ovuParseUTCDate(text) {
  var parts = text.split("-");
  return Date.UTC(parts[2], parts[1] - 1, parts[0]);
}

function ovuParseDate(text) {
  var parts = text.split("-");
  return new Date(parts[2], parts[1] - 1, parts[0]);
}

function ovuDateToStr(date) {
  return date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear();
}

function ovuUTCDateToStr(utc) {
  var date = new Date(utc);
  return date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear();
}

function ovuChartUpdate() {
  if (currentPlot !== undefined) {
    currentPlot.destroy();
  }
  $("#ovu-cycle-chart").empty();
  if (currentCycle !== undefined && currentCycle.days !== undefined && currentCycle.days.length > 0) {
    var temperatureLine = [];
    var minTemperature = 100;
    var maxTemperature = 0;
    $.each(currentCycle.days, function(index, day) {
      temperatureLine.push([ day.date, day.temperature ]);
      minTemperature = Math.min(minTemperature, day.temperature);
      maxTemperature = Math.max(maxTemperature, day.temperature);
    });
    var data = [ temperatureLine ];
    var yscalemin = Math.floor(minTemperature * 10 - 3) / 10;
    var yscalemax = Math.max(Math.ceil(maxTemperature * 10 + 3) / 10, yscalemin + 2);
    var options = {
      axes : {
        xaxis : {
          renderer : $.jqplot.DateAxisRenderer,
          min : currentCycle.startDate,
          // tickInterval : '1 day'
          numberTicks : 14
        },
        yaxis : {
          min : yscalemin,
          max : yscalemax,
          tickInterval : 0.1
        }
      }/*
         * , series : [ { lineWidth : 4, markerOptions : { style : 'square' } } ]
         */,
      highlighter : {
        show : true,
        sizeAdjust : 7.5
      }
    };
    currentPlot = $.jqplot("ovu-cycle-chart", data, options);
  } else {
    $("#ovu-cycle-chart").append('<div class="caption"><p>Dodaj informacje o jednym z dni cyklu aby rozpocząć rysowanie wykresu</p></div>');
  }
}

function ovuCycleChartPointSelected(event, seriesIndex, pointIndex, data) {
  currentDay = data[0];
  $("#ovu-day-details").modal("show");
}

function ovuConnectToGoogleDrive() {
  $("#ovu-jumbo-login-buttons, #ovu-local-storage-notice").css("display", "none");
  $("#ovu-jumbo-goog-load-progress").css("display", "");
  $("#ovu-jumbo-goog-load-progress span").html("Autoryzowanie w usługach Google...");
  gapi.auth.authorize({
    'client_id' : GAPI_CLIENT_ID,
    'scope' : GAPI_SCOPES,
    'immediate' : false
  }, handleGoogleAuthResult);
  window.setTimeout(function() {
    $("#ovu-jumbo-goog-load-progress").css("display", "none");
    $("#ovu-jumbo-login-buttons").css("display", "");
  }, 500);
}

function handleGoogleApiClientLoad() {
  $("#ovu-jumbo-goog-load-progress span").html("Sprawdzanie stanu zalogowania w usługach Google...");
  gapi.client.setApiKey(GAPI_KEY);
  checkGoogleAuth();
}

function checkGoogleAuth() {
  gapi.auth.authorize({
    'client_id' : GAPI_CLIENT_ID,
    'scope' : GAPI_SCOPES.join(' '),
    'immediate' : true
  }, handleGoogleAuthResult);
}

function handleGoogleAuthResult(authResult) {
  if (authResult) {
    $("#ovu-jumbo-goog-load-progress span").html("Pobieranie informacji o koncie Google...");
    gapi.client.request({
      "path" : "/oauth2/v1/userinfo"
    }).execute(function(resp) {
      gapi.client.load('drive', 'v2', function() {
        ovuGoogUpdateAuthStatus(resp);
      });
    });
  } else {
    $("#ovu-jumbo-goog-load-progress").css("display", "none");
    $("#ovu-jumbo-login-buttons").css("display", "");
  }
}

function ovuGoogUpdateAuthStatus(userinfo) {
  if (userinfo) {
    $("#ovu-local-storage-warning").css("display", "none");
    $("#ovu-jumbotron").css("display", "none");
    $("#ovu-breadcrumb").css("display", "");
    $("#ovu-cycle-list-row").css("display", "");
    googUserId = userinfo.id;
    googUserEmail = userinfo.email;
    ovuGoogLoadCycles();
  } else {
    $("#ovu-local-storage-warning").css("display", "");
    ovuRefreshCycleList();
  }
}

function ovuGoogLoadCycles(cycles) {
  // create cycle map
  var cycles = ovuLoadCycles(true);
  var cycleMap = {};
  $.each(cycles, function(index, cycle) {
    cycleMap[cycle.googId] = cycle;
  });
  // fetch files
  $("#ovu-goog-status").html("Pobieranie listy cykli...");
  ovuGoogListFiles(function(files) {
    $('#ovu-cycle-list').empty();
    ovuGoogLoadNextCycle(files, cycleMap, files.length);
  });
}

function ovuGoogLoadNextCycle(files, cycleMap, total) {
  if (files.length > 0) {
    $("#ovu-goog-status").html("Pobieranie danych o cyklu: " + (total - files.length + 1) + "/" + total);
    var file = files.shift();
    var googId = file.id;
    var modified = file.modifiedDate;
    var cycle = cycleMap[googId];
    if (!cycle || !cycle.modified || new Date(cycle.modified) < new Date(modified)) {
      $.ajax({
        url : file.downloadUrl,
        beforeSend : function(xhr) {
          var accessToken = gapi.auth.getToken().access_token;
          xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
        },
        success : function(cycle) {
          cycle.googId = googId;
          cycle.modified = modified;
          ovuSaveCycle(cycle);
          ovuGoogLoadNextCycle(files, cycleMap, total);
        }
      });
    } else {
      delete cycleMap[googId];
      ovuGoogLoadNextCycle(files, cycleMap, total);
    }
  } else {
    $.each(cycleMap, function(googId, cycle) {
      amplify.store(cycle.id, null);
    });
    $("#ovu-goog-status").html("Połączono z kontem Google: " + googUserEmail);
    ovuRefreshCycleList();
  }
}

function ovuGoogListFiles(callback) {
  var retrievePage = function(request, result) {
    request.execute(function(resp) {
      if (resp.items) {
        result = result.concat(resp.items);
      }
      var nextPageToken = resp.nextPageToken;
      if (nextPageToken) {
        request = gapi.client.drive.files.list({
          "pageToken" : nextPageToken
        });
        retrievePage(request, result);
      } else {
        callback(result);
      }
    });
  };
  var initialRequest = gapi.client.drive.files.list({
    "q" : "'appdata' in parents"
  });
  retrievePage(initialRequest, []);
}

function ovuGetSaveFn() {
  var token = gapi.auth.getToken();
  return token ? ovuGoogSyncCycle : ovuLocalSaveCycle;
}

function ovuLocalSaveCycle(cycle, callback) {
  $(".alert-danger").alert('close');
  ovuSaveCycle(cycle);
  callback({
    cycle : cycle
  });
}

function ovuGoogSyncCycle(cycle, callback) {
  $(".alert-danger").alert('close');

  var BOUNDARY = "-------314159265358979323846";

  var contentType = "application/json; charset=UTF-8";
  var metadata = {
    "mimeType" : contentType,
    "parents" : [ {
      "id" : "appdata"
    } ]
  };

  var path = "/upload/drive/v2/files";
  var method = "POST";
  if (cycle.googId) {
    path += "/" + cycle.googId;
    method = "PUT";
    delete cycle.googId;
  }

  var requestBody = "";
  requestBody += "\r\n--" + BOUNDARY + "\r\n";
  requestBody += "Content-Type: application/json\r\n\r\n";
  requestBody += $.toJSON(metadata);
  requestBody += "\r\n--" + BOUNDARY + "\r\n";
  requestBody += "Content-Type: " + contentType + "\r\n\r\n";
  requestBody += $.toJSON(cycle);
  requestBody += "\r\n--" + BOUNDARY + "--";

  var request = gapi.client.request({
    "path" : path,
    "method" : method,
    "params" : {
      "uploadType" : "multipart"
    },
    "headers" : {
      "Content-Type" : 'multipart/mixed; boundary="' + BOUNDARY + '"'
    },
    "body" : requestBody
  }).execute(function(file) {
    var resp = {};
    if (!file.error) {
      cycle.googId = file.id;
      cycle.modified = file.modifiedDate;
      resp.cycle = cycle;
    } else {
      resp.error = file.error;
    }
    ovuSaveCycle(cycle);
    callback(resp);
  });
}
